package ln.fxspringarchetype.controller.student;

import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import ln.fxspringarchetype.controller.BaseFxController;
import ln.fxspringarchetype.dialog.student.AddStudentDialog;
import ln.fxspringarchetype.dialog.student.DeleteStudentDialog;
import ln.fxspringarchetype.dialog.student.EditStudentDialog;
import ln.fxspringarchetype.mapper.StudentMapper;
import ln.fxspringarchetype.model.Student;
import org.apache.logging.log4j.util.Strings;
import org.controlsfx.control.Notifications;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import java.io.ByteArrayInputStream;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;

@Controller
public class StudentController extends BaseFxController {
    /**
     * 当前页码
     */
    private final IntegerProperty pageNum = new SimpleIntegerProperty();
    /**
     * 每页记录数
     */
    private final IntegerProperty pageSize = new SimpleIntegerProperty();
    /**
     * 所有记录的总页数
     */
    private final IntegerProperty pageCount = new SimpleIntegerProperty();
    /**
     * 查询关键字
     */
    private final StringProperty keyword = new SimpleStringProperty();
    /**
     * 列表中的记录集
     */
    private final ObjectProperty<ObservableList<Student>> studentRecords = new SimpleObjectProperty<>();

    @FXML
    private TextField searchBox;
    @FXML
    private ChoiceBox<Integer> pageSizeChoice;
    @FXML
    private TableColumn<Student, Integer> idColumn;
    @FXML
    private TableColumn<Student, String> nameColumn;
    @FXML
    private TableColumn<Student, String> genderColumn;
    @FXML
    private TableColumn<Student, String> birthdayColumn;
    @FXML
    private TableColumn<Student, byte[]> photoColumn;
    @FXML
    private Pagination pagination;
    @FXML
    private TableView<Student> studentTable;

    @Autowired
    private StudentMapper studentMapper;

    /**
     * 设置值工厂
     * 为每个列提供数据
     */
    @Override
    public void setCellValueFactories() {
        idColumn.setCellValueFactory(new PropertyValueFactory<>("id"));
        nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
        genderColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getGender().getValue()));
        //以LocalDate的格式输出
        birthdayColumn.setCellValueFactory(param -> {
            Date date = param.getValue().getBirthday();
            LocalDate localDate = LocalDate.ofInstant(date.toInstant(), ZoneId.systemDefault());
            return new SimpleStringProperty(localDate.toString());
        });
        photoColumn.setCellValueFactory(new PropertyValueFactory<>("photo"));
    }

    /**
     * 设置单元格的显示方式
     */
    @Override
    public void setCellFactories() {
        //一般单元格都是以标签Label呈现，照片的单元格需要特殊的形式
        photoColumn.setCellFactory(column -> new PhotoTableCell());
    }

    /**
     * 建立绑定关系
     * 页码与每页记录数变化时自动重载数据
     */
    @Override
    public void createBindings() {
        pageNum.bind(pagination.currentPageIndexProperty());
        pageSize.bind(pageSizeChoice.valueProperty());
        keyword.bind(searchBox.textProperty());
        //根据页码、记录数、查询关键词自动加载学生信息
        studentRecords.bind(
                Bindings.createObjectBinding(
                        () -> {
                            //为页号与每页记录数过滤无效值
                            int pNum = pageNum.getValue();
                            int pSize = pageSize.getValue();
                            String kw = keyword.getValue();
                            if (pNum < 0 || pSize <= 0) {
                                return FXCollections.emptyObservableList();
                            }
                            //无查询参数时执行简单分页，否则执行按名称分页查询
                            if (Strings.isEmpty(kw)) {
                                return FXCollections.observableList(studentMapper.findPage(pNum, pSize));
                            } else {
                                return FXCollections.observableList(studentMapper.findPageByName(kw, pNum, pSize));
                            }
                        }, pageNum, pageSize, keyword));
        studentTable.itemsProperty().bind(studentRecords);

        //根据页码、记录数、查询关键词自动计算结果页数
        pageCount.bind(Bindings.createIntegerBinding(
                () -> {
                    int pNum = pageNum.getValue();
                    int pSize = pageSize.getValue();
                    String kw = keyword.getValue();
                    if (pNum < 0 || pSize <= 0) {
                        return 0;
                    }
                    if (Strings.isEmpty(kw)) {
                        return studentMapper.pageCount(pSize);
                    } else {
                        return studentMapper.pageCountByName(kw, pSize);
                    }
                }, pageNum, pageSize, keyword));

        pagination.pageCountProperty().bind(pageCount);
    }

    @Override
    public void initLayout() {

    }

    /**
     * 加载学生信息数据
     */
    @Override
    public void loadData() {
        pagination.setCurrentPageIndex(0);
        pageSizeChoice.getSelectionModel().selectFirst();
    }

    /**
     * 重新加载表格数据
     */
    private void reload() {
        //设为<0的值可以让分页控件自动设置页码到0,从而触发自动加载逻辑
        pagination.setCurrentPageIndex(-1);
    }

    @FXML
    private void addStudent() {
        AddStudentDialog addStudentDialog = new AddStudentDialog();
        Optional<Student> insertedStudent = addStudentDialog.showAndWait();
        //点下了确定键返回新建对象
        if (insertedStudent.isPresent()) {
            int inserted = studentMapper.insert(insertedStudent.get());
            Notifications notifications;
            if (inserted > 0) {
                notifications = showPrompt("新增学生信息成功",
                        String.format("学生 %s 的信息已经成功添加。", insertedStudent.get().getName()));
                notifications.showInformation();
            } else {
                notifications = showPrompt("新增学生信息失败",
                        String.format("学生 %s 的信息未能成功添加。", insertedStudent.get().getName()));
                notifications.showError();
            }
            reload();
        }
    }

    @FXML
    private void deleteStudent() {
        Student student = studentTable.getSelectionModel().getSelectedItem();
        if (Objects.isNull(student)) {
            Notifications notifications = showPrompt("没有选择学生信息", "请选择一条学生信息！");
            notifications.showInformation();
            return;
        }
        DeleteStudentDialog deleteStudentDialog = new DeleteStudentDialog();
        //加载待删除记录到对话框里
        deleteStudentDialog.loadStudent(student);
        Optional<Student> deletedStudent = deleteStudentDialog.showAndWait();
        if (deletedStudent.isPresent()) {
            int deleted = studentMapper.delete(student.getId());
            Notifications notifications;
            if (deleted > 0) {
                notifications = showPrompt("删除学生信息成功",
                        String.format("学生 %s 的信息已经成功删除。", student.getName()));
                notifications.showInformation();
            } else {
                notifications = showPrompt("删除学生信息失败",
                        String.format("学生 %s 的信息没有成功删除。", student.getName()));
                notifications.showError();
            }
            reload();
        }
    }

    @FXML
    private void editStudent() {
        Student student = studentTable.getSelectionModel().getSelectedItem();
        if (Objects.isNull(student)) {
            showPrompt("没有选择学生信息", "请选择一条学生信息！");
            return;
        }
        EditStudentDialog editStudentDialog = new EditStudentDialog();
        editStudentDialog.loadStudent(student);
        Optional<Student> editedStudent = editStudentDialog.showAndWait();
        if (editedStudent.isPresent()) {
            int edited = studentMapper.update(editedStudent.get());
            Notifications notifications;
            if (edited > 0) {
                notifications = showPrompt("修改学生信息成功", String.format("学生 %s 的信息已经成功修改。", student.getName()));
                notifications.showInformation();
            } else {
                notifications = showPrompt("修改学生信息失败", String.format("学生 %s 的信息未能成功修改。", student.getName()));
                notifications.showError();
            }
            reload();
        }
    }

    /**
     * 在表格中使用的照片单元格
     */
    private static class PhotoTableCell extends TableCell<Student, byte[]> {
        /**
         * 照片的宽度
         */
        private static final int photo_width = 64;
        /**
         * 照片的高度
         */
        private static final int photo_height = 64;

        @Override
        protected void updateItem(byte[] item, boolean empty) {
            super.updateItem(item, empty);
            //只更新有数据的单元格
            if (!empty) {
                Image image = new Image(new ByteArrayInputStream(item));
                ImageView imageView = new ImageView(image);
                imageView.setFitWidth(photo_width);
                imageView.setFitHeight(photo_height);
                imageView.setPreserveRatio(true);
                setGraphic(imageView);
            } else {
                //擦除之前有数据的行留下的ImageView
                setGraphic(null);
            }
        }
    }
}
